<?php
/**
 * 内部调用的函数集
 * 
 * @package if
 * @subpackage vm
 * @since 1.0 
 */

/**
 * 自动加载类
 *
 * @param string $class 类
 * @since 1.0
 */
function if_autoload($class) {
	if (class_exists($class, false)) {
		return;
	}
	if (!class_exists("IVM", false)) {
		require(dirname(__FILE__) . "/IVM.php");
	}
	$file = IVM::singleton()->findClass($class);
	if ($file) {
		require_once($file);
	}
}

/**
 * 判断一个对象/类是否是一个类的实例
 *
 * @param object|string $object 对象或类名
 * @param string $class 类名
 * @return boolean
 * @since 1.0
 */
function if_is_instance_of($object, $class) {
	if (!is_object($object) && !is_string($object)) {
		return false;
	}
	return ($object instanceof $class);
}

/**
 * 给一个数组的每一个值都加上前缀
 *
 * @param array $array 数组
 * @param string $prefix 前缀
 * @since 1.0
 */
function if_add_prefix(&$array, $prefix) {
	array_walk($array, "_addPrefixCallback", $prefix);
}

/**
 * if_add_prefix的回调函数
 *
 * @param string $value 数组元素值
 * @param string $key 数组键值
 * @param string $prefix 前缀
 * @since 1.0
 */
function _addPrefixCallback(&$value, $key, $prefix) {
	$value = $prefix . $value;
}

/**
 * 合并两个数组
 * 
 * $array2的索引将会被取消
 *
 * @param array $array1 数组1
 * @param array $array2 数组2
 * @param boolean $unique 是否进行unique处理
 * @since 1.0
 */
function if_array_join(array $array1, array $array2, $unique = false) {
	foreach ($array2 as $value) {
		$array1[] = $value;
	}
	
	return $unique?array_unique($array1):$array1;
}

/**
 * 定义一个不可编译的文件名
 * 
 * 和if_compilable相对
 *
 * @param string $filename 可以是文件名，也可以是一个正则表达式，如 /smarty
 * @since 1.0
 */
function if_uncompilable($filename) {
	$filename = preg_replace("{/+}", DIRECTORY_SEPARATOR, $filename);
	if (!isset($GLOBALS["COMPILABLE"]) || !is_array($GLOBALS["COMPILABLE"])) {
		$GLOBALS["COMPILABLE"] = array("no" => array());
	}
	else {
		$GLOBALS["COMPILABLE"]["no"] = (isset($GLOBALS["COMPILABLE"]["no"]) && is_array($GLOBALS["COMPILABLE"]["no"]))?$GLOBALS["COMPILABLE"]["no"]:array();
	}
	$GLOBALS["COMPILABLE"]["no"][] = $filename;
}


/**
 * 定义一个可编译的文件名
 * 
 * 和if_uncompilable相对
 *
 * @param string $filename 可以是文件名，也可以是一个正则表达式，如 /smarty
 * @since 1.0
 */
function if_compilable($filename) {
	$filename = preg_replace("{/+}", DIRECTORY_SEPARATOR, $filename);
	if (!isset($GLOBALS["COMPILABLE"]) || !is_array($GLOBALS["COMPILABLE"])) {
		$GLOBALS["COMPILABLE"] = array("yes" => array());
	}
	else {
		$GLOBALS["COMPILABLE"]["yes"] = (isset($GLOBALS["COMPILABLE"]["yes"]) && is_array($GLOBALS["COMPILABLE"]["yes"]))?$GLOBALS["COMPILABLE"]["yes"]:array();
	}
	$GLOBALS["COMPILABLE"]["yes"][] = $filename;
}

if (function_exists("spl_autoload_register")) {
	spl_autoload_register("if_autoload");
}
else {
	function __autoload($class) {
		if_autoload($class);
	}
}

/**
 * 取得一个路径对应的唯一键值
 *
 * 用来指定编译文件位置
 * 
 * @param string $path 路径
 * @return string
 * @since 1.0
 */
function if_unique_key_of_path($path) {
	if (isset($GLOBALS["IF_UNIQUE_KEY"])) {
		return $GLOBALS["IF_UNIQUE_KEY"];
	}
	$compileIds = if_array_from_compiler("UNIQUE_KEY");
	$path = __ROUTER__ . $path;
	foreach ($compileIds as $pattern => $id) {
		if (preg_match("{" . $pattern . "}i", $path)) {
			$GLOBALS["IF_UNIQUE_KEY"] = "/" . $id;
			return "/" . $id;
		}
	}
	return null;
}

/**
 * 启动编译器
 *
 * 要想在控制器级别阻止系统生成编译文件，可以使用以下代码：
 * <code>
 * $GLOBALS["IF_COMPILER_SHUT"] = 1;
 * </code>
 * 
 * @param string $dir 编译文件生成位置
 * @since 1.0
 */
function if_start_compiler($dir) {
	if (!IF_COMPILER_ENABLED) {
		return;
	}
	
	//尝试新建
	if (!is_dir($dir)) {
		if (!@mkdir($dir, 0777)) {
			//exit("you must create directory '" . $dir . "' for compiler");
		}
	}
	if (!is_writable($dir) || !isset($_SERVER["REQUEST_URI"])) {
		return;
	}

	//编译类文件
	$GLOBALS["COMPILABLE"] = array("yes" => array(), "no" => array());
	$regFwDir = str_replace("\\", "\\\\", IF_FW_DIR. "(.+)" . DIRECTORY_SEPARATOR);
	$regFwDir = str_replace("-", "\-", $regFwDir);
	if_compilable("{" . $regFwDir . "[A-Z][a-zA-Z0-9]+\.php}");
	
	//读取配置
	$GLOBALS["COMPILED_CONFIG"] = array();
	$domainName = if_domain_name();
	$compiledConfigFile = $dir . "/" . "~" . urlencode($domainName . __ROUTER__) . ".config-" . IF_VERSION . "-" . IF_APP_ENV . ".php";
	if (is_file($compiledConfigFile)) {
		$config = require($compiledConfigFile);
		if (is_array($config)) {
			$GLOBALS["COMPILED_CONFIG"] = $config;
		}
	}
	
//	if (IF_APP_ENV == "prod") {
//		return;
//	}
	
	//加载已经编译的类文件
	$path = if_unique_key_of_path(IF_ACTION_PATH);
	if (!$path) {
		return;
	}
	$path = $domainName . $path;
	
	//是否有更新
	$path = urlencode($path . $GLOBALS["__ROUTER__"]);
	$compiledCodeFile = $dir . "/" . "~{$path}." . IF_VERSION . "." . IF_COMPILER_EXT;
	if (is_file($compiledCodeFile)) {
		$time = filemtime($compiledCodeFile);
		$current = time();
		$renew = false;
		if ($time + IF_COMPILER_CLASS_TTL < $current) {
			unlink($compiledCodeFile);
			if_log("unlink '{$compiledCodeFile}'");
			$renew = true;
		}

		//是否有更新
		if (!empty($GLOBALS["COMPILED_CLASSES"])) {
			foreach ($GLOBALS["COMPILED_CLASSES"] as $file => $mtime) {
				if (!is_file($file) || filemtime($file) != $mtime) {
					unlink($compiledCodeFile);
					if_log("unlink '{$compiledCodeFile}'");
					$renew = true;
					break;
				}
			}
		}

		if (!$renew) {
			require($compiledCodeFile);
			if_log("require from compiled code");
		}
		
		$GLOBALS["IF_COMPILER_EXPIRED"] = $renew;
	}
}

/**
 * 清空编译文件
 * 
 * 可以用在升级完框架或重新生成配置时清空缓存文件。
 * 
 * v1.1.2起不删除 .const.php 文件
 *
 * @since 1.0
 */
function if_clean_compiler_dir() {
	$dir = IF_COMPILER_DIR;
	if (is_dir($dir)) {
		$handler = opendir($dir);
		while (($file = readdir($handler)) !== false) {
			if ($file != "." && $file != ".." && $file != "" && $file{0} == "~") {
				if (!preg_match("/\.const\.php$/", $file) && !@unlink($dir . "/" . $file)) {
					throw new Exception("can't delete compiled file " . ($dir . "/" . $file), 0x100138);
				}
			}
		}
		closedir($handler);
	}
	
	$GLOBALS["IF_COMPILER_SHUT"] = 1;//阻止生成编译文件
}

/**
 * 取得当前的域名
 *
 * @return string|null
 * @since 1.0
 */
function if_domain_name() {
	if (isset($_SERVER["HTTP_HOST"])) {
		return $_SERVER["HTTP_HOST"];
	}
	if (isset($_SERVER["SERVER_NAME"])) {
		return $_SERVER["SERVER_NAME"];
	}
}

/**
 * 关闭编译器
 * 
 * 让编译器不再工作，从而实现刷新编译文件的目的
 * 
 * @param string $dir 编译文件生成位置
 * @since 1.1.2
 */
function if_shut_compiler() {
	$GLOBALS["IF_COMPILER_SHUT"] = 1;
}

/**
 * 结束编译器并对上下文的类、配置进行处理
 * 
 * issues:
 *  * 类文件中的__FILE__将会被转换成实际文件路径 -- '/path/of/file'
 *  * 类文件中的__LINE__将不能正常工作
 * 
 * @param string $dir 编译文件生成位置
 * @since 1.1.2
 */
function if_commit_compiler($dir) {
	if (isset($GLOBALS["IF_COMPILER_SHUT"])) {
		return;
	}
	$GLOBALS["IF_COMPILER_SHUT"] = 1;
	if (!IF_COMPILER_ENABLED || !is_writable($dir) || !isset($_SERVER["REQUEST_URI"])) {
		return;
	}
	$domainName = if_domain_name();
	
	//加入uniqueKey规则
	if (!empty($GLOBALS["COMPILER_UNIQUE_KEY"])) {
		$uniquePath = $GLOBALS["COMPILER_UNIQUE_KEY"]["path"];
		$uniqueKey = $GLOBALS["COMPILER_UNIQUE_KEY"]["key"];
		$compilerIds = if_array_from_compiler("UNIQUE_KEY");
		if (!isset($compilerIds[$uniquePath])) {
			$compilerIds[$uniquePath] = $uniqueKey;
			if_array_to_compiler("UNIQUE_KEY", $compilerIds);
		}
	}
	
	//配置文件是否过期
	$compiledConfigFile = $dir . "/" . "~" . urlencode($domainName . __ROUTER__) . ".config-" . IF_VERSION . "-" . IF_APP_ENV . ".php";
	if (is_file($compiledConfigFile) && filemtime($compiledConfigFile) + IF_COMPILER_CONFIG_TTL < time()) {
		@unlink($compiledConfigFile);
		if_log("unlink '{$compiledConfigFile}'");
		$GLOBALS["IF_COMPILER_EXPIRED"] = true;
	}
	else if (isset($GLOBALS["COMPILED_CONFIG"]) && is_array($GLOBALS["COMPILED_CONFIG"]) && isset($GLOBALS["COMPILED_CONFIG"]["IF_MODIFIED"]) && $GLOBALS["COMPILED_CONFIG"]["IF_MODIFIED"]) {
		$GLOBALS["COMPILED_CONFIG"]["IF_MODIFIED"] = false;
		if (!is_file($compiledConfigFile) || is_writeable($compiledConfigFile)) {
			if_write_file($compiledConfigFile, "<?php \n" . "if (!defined(\"IF_VERSION\")) {exit();}\n" . "return " . var_export($GLOBALS["COMPILED_CONFIG"], true) . ";\n?>", true);
			if_log("write '{$compiledConfigFile}'");
		}
	}
	
	//编译文件是否过期
	if (isset($GLOBALS["IF_COMPILER_EXPIRED"]) && $GLOBALS["IF_COMPILER_EXPIRED"]) {
		return;
	}
	
	$path = if_unique_key_of_path(IF_ACTION_PATH);
	if (!$path) {
		return;
	}
	$path = $domainName . $path;
	$path = urlencode($path . __ROUTER__);
	$compiledCodeFile = $dir . "/" . "~{$path}." . IF_VERSION . "." . IF_COMPILER_EXT;
	//if (IF_APP_ENV == "prod" && is_file($compiledCodeFile)) {//v1.1.0如果环境是prod而且文件存在，则直接不检查
		//return;
	//}
	
	
	//引入的类文件
	$classes = get_required_files();
	
	//查找要编译的文件
	$compiledCode = "";
	$compiledFiles = array();
	$codes = array();
	foreach ($classes as $class) {
		$compilable = false;

		foreach ($GLOBALS["COMPILABLE"]["yes"] as $include) {
			if ($include == $class || ($include != "" && $include{0} == "{" && preg_match($include, $class))) {
				$compilable = true;
				break;
			}
		}
		foreach ($GLOBALS["COMPILABLE"]["no"] as $except) {
			if ($except == $class || ($except != "" && $except{0} == "{" && preg_match($except, $class))) {
				$compilable = false;
				break;
			}
		}
		if ($compilable) {
			if (!isset($GLOBALS["COMPILED_CLASSES"]) || !isset($GLOBALS["COMPILED_CLASSES"][$class])) {
				$compiledFiles[$class] = filemtime($class);
				//应尝试使用php_strip_whitespace函数
				$code = file_get_contents($class);
				$code = trim($code);
				$code = trim($code, "?<>php");
				$code = preg_replace("{/\*\*(.+)\*/}sU", "", $code);
				$code = preg_replace("/\n\s*public\s+function/", "\nfunction", $code);
				$code = preg_replace("/\n\s*public\s+static/", "\nstatic", $code);
				$code = preg_replace("/\n\s+/s", "\n", $code);
				$code = str_ireplace("if_compilable(__FILE__);", "", $code);
				$code = str_ireplace("if_uncompilable(__FILE__);", "", $code);
				$code = preg_replace("{\n//.+}", "", $code);
				//处理__FILE__
				$code = str_replace("__FILE__", "'" . $class . "'", $code);
				$codes[] = $code . "\n";
			}
		}
		$compilable = false;
	}

	if (!empty($codes)) {
		//重新排序
		$result = array();
		
		//初次
		$chains = array();//类之间连接
		$classes = array();
		foreach ($codes as $key => $code) {
			$chain = if_parse_class_from_code($code);
			if (empty($chain)) {//没有class定义
				$result[] = $code;
				unset($codes[$key]);
			}
			else {
				$classes[] = $chain["class"];
				if (empty($chain["parent"])) {
					$result[$chain["class"]] =  $code;
					$chains[$chain["class"]] = array("chain" => $chain, "code" => $code);
				}
				else {
					$chains[$chain["class"]] = array("chain" => $chain, "code" => $code);
					foreach ($chain["parent"] as $parent) {
						$classes[] = $parent;
					}
				}
			}
		}
		$classes = array_unique($classes);
		foreach ($classes as $class) {
			if (!isset($chains[$class])) {
				$result[$class] = "";
			}
		}
	
		
		//查找
		for ($i = 0; $i < 10; $i ++) {
			if (empty($chains)) {
				break;
			}
			foreach ($chains as $key => $chainAndCode) {
				$chain = $chainAndCode["chain"];
				$parentLoaded = true;
				foreach ($chain["parent"] as $parent) {
					if (!isset($result[$parent])) {
						$parentLoaded = false;
						break;
					}
				}
				if ($parentLoaded) {
					$result[$chain["class"]] = $chainAndCode["code"];
				}
			}
		}
		$compiledCode = implode("", $result);
	}
	else {
		$compiledCode = "";
	}

	
	
	if (is_file($compiledCodeFile)) {
		if ($compiledCode) {
			unlink($compiledCodeFile);
			if_log("unlink '{$compiledCodeFile}'");
		}
	}
	else if ($compiledCode != "" && (!is_file($compiledCodeFile) || is_writeable($compiledCodeFile))) {
		//缓存的files
		if_write_file($compiledCodeFile, "<?php\n" . "if (!defined(\"IF_VERSION\")) {exit();}\n" . "\$GLOBALS['COMPILED_CLASSES']=" . var_export($compiledFiles, true) . ";\n" . $compiledCode . "\n?>");
		if_log("write '{$compiledCodeFile}'");
	}
}


/**
 * 从代码中分析类的定义
 * 
 * 可以分析以下定义：
 * 	class A
	class A extends B
	class A implements B
	class A extends B implements C,D
	interface A extends B
	abstract class A extends B implements C,D
	
 * @param string $code 类代码
 * @return array
 * @since 1.0
 */
function if_parse_class_from_code($code) {
	$ret = preg_match("/(abstract\s+)?(class|interface)\s+(\w+)(\s+extends\s+(\w+))?(\s+implements\s+((\w+)(\s*,\s*(\w+))*))?\s*\{/i", $code, $match);
	if ($ret) {
		$class = $match[3];
		$parent = array();
		if (isset($match[5]) && $match[5] != "") {
			$parent[] = $match[5];
		}
		if (isset($match[7])) {
			$interfaces = explode(",", $match[7]);
			foreach ($interfaces as $interface) {
				$parent[] = trim($interface);
			}
		}
		return array("class" => $class, "parent" => $parent);
	}
	return array();
}

/**
 * 将数组放入编译器中
 *
 * @param string $key 键
 * @param string $value 值
 * @return array
 * @since 1.0
 */
function if_array_to_compiler($key, $value) {
	if (!isset($GLOBALS["COMPILED_CONFIG"]["USER_DEFINED"])) {
		$GLOBALS["COMPILED_CONFIG"]["USER_DEFINED"] = array();
	}
	if (is_array($value)) {
		$GLOBALS["COMPILED_CONFIG"]["USER_DEFINED"][$key] = $value;
		$GLOBALS["COMPILED_CONFIG"]["IF_MODIFIED"] = true;
		return $value;
	}
	return array();
}

/**
 * 从编译器中得到数组
 *
 * @param string $key 键
 * @return array
 * @since 1.0
 */
function if_array_from_compiler($key) {
	if (!isset($GLOBALS["COMPILED_CONFIG"]["USER_DEFINED"])) {
		return array();
	}
	if (!isset($GLOBALS["COMPILED_CONFIG"]["USER_DEFINED"][$key])) {
		return array();
	} 
	return $GLOBALS["COMPILED_CONFIG"]["USER_DEFINED"][$key];
}

/**
 * 判断一个路径是否是绝对路径
 *
 * @param string $path 路径
 * @return boolean
 * @since 1.0
 */
function if_is_absolute_path($path) {
	$path = trim($path);
	if (if_empty($path)) {
		return false;
	}
	return ($path{0} == "/" || $path{0} == "\\" || in_array(substr($path, 1, 2), array(":\\", ":/")));
}

/**
 * 判断一个字符串是否可为有效的变量名
 *
 * @param string $variableName 准变量名
 * @return boolean
 * @since 1.0
 */
function if_is_valid_variable_name($variableName) {
	if (preg_match("/^[_a-z]\\w*$/i", $variableName)) {
		return true;
	}
	return false;
}

/**
 * 判断value是否为nil
 *
 * @param mixed $value 值
 * @return boolean
 * @since 1.0
 */
function if_is_nil($value) {
	return $value === nil;
}

/**
 * 将名称转换为Java的编程风格
 *
 * 首字母小写，其余单词的首字母大写<br/>
 * load_xml_config --> loadXmlConfig
 * 
 * @param string $name 名称
 * @return string
 * @since 1.0
 */
function if_name_to_java($name) {
	$name = preg_replace("/_([a-zA-Z])/e", "strtoupper('\\1')", $name);
	return $name;
}

/**
 * 将名称转换为C的编码风格
 * 
 * 字母小写，单词之间有中划线<br/>
 * - loadXmlConfig --> load_xml_config
 * - LoadXmlConfig -> load_xml_config
 * - _loadXmlConfig --> _load_xml_config 
 * - _LoadXmlConfig --> _load_xml_config
 * 
 * 从1.0.3开始，如果名称中的大写字母前面已经有下划线（_），则此字母前不再新增一个下划线
 *
 * @param string $name 名称
 * @return string
 * @since 1.0
 */
function if_name_to_c($name) {
	$name = strval($name);
	if (!$name) {
		return $name;
	}
	$name = preg_replace("/([^_\b])([A-Z])/", "\\1_\\2", $name);
	return strtolower($name);
}

/**
 * 取得class的真实名称
 * 
 * if_class_name("a.b.c.MyClass") -> MyClass
 * 
 * @param string $class 可能含有包的类名
 * @return string
 * @since 1.0
 */
function if_class_name($class) {
	if ($class == "") {
		return null;
	}
	$pieces = explode(".", $class);
	return array_pop($pieces);
}

/**
 * 取得$_SERVER中的参数值
 *
 * @return string|null
 * @since 1.0
 */
function if_server($param) {
	return isset($_SERVER[$param])?$_SERVER[$param]:null;
}

/**
 * 取得$_ENV中的参数值
 *
 * @return string|null
 * @since 1.0
 */
function if_env($param) {
	return isset($_ENV[$param])?$_ENV[$param]:null;
}

/**
 * 判断是否支持重写规则
 *
 * @return integer 0|1
 * @since 1.0
 */
function if_rewrite() {
	if (defined("IF_REWRITED")) {
		return IF_REWRITED;
	}
	if (isset($_GET["IF_REWRITE"])) {
		unset($_GET["IF_REWRITE"]);
		unset($GLOBALS["HTTP_GET_VARS"]["IF_REWRITE"]);
		return 1;
	}
	return 0;
}

/**
 * 从数组中提取某个键的值，重新组织一个新的数组
 *
 * @param array $array 输入的数组
 * @param string|integer $key 键名
 * @param boolean $keepIndex 是否保留索引
 * @return array
 * @since 1.0
 */
function if_array_pick($array, $key, $keepIndex = false) {
	if (!is_array($array)) {
		return array();
	}
	$ret = array();
	foreach ($array as $index => $row) {
		if (is_array($row) && array_key_exists($key, $row)) {
			if ($keepIndex) {
				$ret[$index] = $row[$key];
			}
			else {
				$ret[] = $row[$key];
			}
		}
	}
	return $ret;
}

/**
 * 从一个数组的值中选取key做当前数组的key
 * 
 * <code>
 * $array = array(
 *   array("a" => 11, "b" => 12),
 *   array("a" => 21, "b" => 22)
 *   //...
 * );
 * 
 * $array2 = if_array_combine($array, "a", "b");
 * </code>
 * 
 * $array2就变成了：
 * <code>
 * array(
 *   11 => 12,
 *   21 => 22
 * );
 * </code>
 * 
 * 如果$valueName没有值，则是把当前元素值当成value:
 * 
 * <code>
 * $array2 = if_array_combine($array, "a");
 * 
 * array(
 *   11 => array("a" => 11, "b" => 12),
 *   21 => array("a" => 21, "b" => 22)
 * );
 * </code>
 * 
 * 从1.0.3开始起，支持以点(.)符号连接的多层次keyName和valueName：
 * - if_array_combine($array, "a.b", "a.c"); 
 * 即重新构成了一个以$array[n][a][b]为键，以$array[n][a][c]为值的数组，其中n是数组的索引
 *
 * @param array $array 二维数组
 * @param integer|string $keyName 选取的key名称
 * @param integer|string $valueName 选取的值名称
 * @return array
 * @since 1.0
 */
function if_array_combine($array, $keyName, $valueName = null) {
	$ret = array();
	foreach ($array as $row) {
		if (is_array($row)) {
			$keyValue = if_array_get($row, $keyName);
			$value = is_null($valueName) ? $row : if_array_get($row, $valueName);
			if ($keyValue) {
				$ret[$keyValue] = $value;
			}
			else {
				$ret[] = $value;
			}
		}
	}
	return $ret;
}

/**
 * 分析组件
 *
 * @since 1.0
 */
function if_parse_component() {
	$components = array();
	if (isset($GLOBALS["IF_COMPONENTS"]) && is_array($GLOBALS["IF_COMPONENTS"])) {
		$components = $GLOBALS["IF_COMPONENTS"];
	}
	else {
		$components = array();
	}
	
	//分析组件
	$hasComponent = false;
	if (!empty($components)) {
		foreach ($components as $config) {
			if (isset($config["prefix"]) && isset($config["dir"])) {
				if (preg_match("{^" . preg_quote($config["prefix"]) . "(/.*)?$}i", __PATH__, $match)) {
					$hasComponent = true;
					$GLOBALS["IF_ACTION_PATH"] = $match[1];
					$GLOBALS["__ROUTER__"] = $GLOBALS["__ROUTER__"] . $config["prefix"];
					$GLOBALS["IF_COMPONENT_DIR"] = $config["dir"];
				}
			}
		}
	}
	
	//自动匹配组件
	if (!$hasComponent && __PATH__ != "") {
		$path = ltrim(__PATH__, "/");
		if (substr($path, 0, 1) == "@") {
			$slash = strpos($path, "/");
			$componentName = $slash?substr($path, 1, $slash - 1):substr($path, 1);
			$GLOBALS["IF_ACTION_PATH"] = substr($path, strlen($componentName) + 1);
			$GLOBALS["__ROUTER__"] = $GLOBALS["__ROUTER__"] . "/" . "@" . $componentName;
			$GLOBALS["IF_COMPONENT_DIR"] = $componentName;
			$hasComponent = true;
		}
	}
	$GLOBALS["IF_HAS_COMPONENT"] = $hasComponent;
}

/**
 * 记录日志
 * 
 * 仅仅用于IF核心的调试
 *
 * @param string $msg 消息
 * @since 1.0
 */
function if_log($msg) {
	if (defined("IF_VM_LOG") && IF_VM_LOG == "on") {
		$fp = fopen("if.log", "a+");
		fwrite($fp, date("Y-m-d H:i:s") . "\n" . $msg . "\n\n");
		fclose($fp);
	}
}

/**
 * 程序关闭回调函数
 *
 * @since 1.0
 */
function if_shutdown() {
	if (class_exists("ILog")) {
		ILog::singleton()->stdout();
	}
	if_commit_compiler(IF_COMPILER_DIR);
}

/**
 * 判断值是否为空
 * 
 * 如果值为以下中的一个，则视为空：
 * -   nil
 * -   IEmptyObject的实例
 * -   使用empty函数判断为空的值
 * 
 * 与empty不同的是，如果$strict为true, 将只含有0字符的字符串视为非空
 *
 * @param mixed $value 值
 * @param boolean $strict 是否严格比较，在1.0.1中加入
 * @return boolean
 * @since 1.0
 */
function if_empty($value, $strict = true) {
	if (if_is_nil($value)) {
		return true;
	}
	if (if_is_instance_of($value, "IEmptyObject")) {
		return true;
	}
	if ($strict && is_string($value) && $value === "0") {
		return false;
	}
	return empty($value);
}

/**
 * 取得一个数组中中某个键的值
 * 
 * 从1.1.2开始，如果键中也含有点(.)符号，那么需要进行转义：
 * <code>
 * p(o("@a.b\.c.d");
 * </code>
 * 其中b.c是完整的键，为了不被分开，需要用反斜杆转义。
 * 
 * 这条规则对于所有配置的值的获取方法都是适应的，比如：
 * 在_global.php中配置：
 * <code>
 * return array(
 * 	"index.add" => array(
 * 		"action" => "增加动作"
 * 	)
 * );
 * </code>
 * 然后再程序中就可以使用
 * <code>
 * p(o("@global.index\.add.action"));
 * </code>
 * 获取该配置选项的值。
 * 
 * 也可以使用一对反引号（`）进行批量的转义：
 * <code>
 * p(o("@global.`index.add`.action"));
 * </code>
 * 这样index.add就是一个完整的键。
 * 
 * if_array_set()函数同样适用此规则。
 *
 * @param array $array 数组
 * @param array|string $keys 键，可以是多级的，比如a.b.c
 * @return mixed
 * @since 1.0
 * @see if_array_set
 */	
function if_array_get(array $array, $keys) {
	if (is_array($keys) && empty($keys)) {
		return $array;
	}
	if (!is_array($keys)) {
		if (strstr($keys, "`")) {
			$keys = preg_replace("/`(.+)`/Ue", "str_replace('.','\.','\\1')", $keys);
		}
		$keys = preg_split("/(?<!\\\)\./", $keys);
	}
	if (count($keys) == 1) {
		$firstKey = array_pop($keys);
		$firstKey = str_replace("\.", ".", $firstKey);
		return array_key_exists($firstKey, $array)?$array[$firstKey]:null;
	}
	$lastKey = array_pop($keys);
	$lastKey = str_replace("\.", ".", $lastKey);
	$lastArray = $array;
	foreach ($keys as $key) {
		$key = str_replace("\.", ".", $key);
		if (is_array($lastArray) && array_key_exists($key, $lastArray)) {
			$lastArray = $lastArray[$key];
		}
		else {
			return null;
		}
	}
	
	return (is_array($lastArray) && array_key_exists($lastKey, $lastArray))? $lastArray[$lastKey] : null; 	
}

/**
 * 设置一个数组中某个键的值，并返回设置后的值
 * 
 * 对原有的数组没有影响
 *
 * @param array $array 数组
 * @param array|string $keys 键，可以是多级的，比如a.b.c
 * @param mixed $value 新的键值
 * @return array
 * @since 1.0
 * @see if_array_get
 */
function if_array_set(array $array, $keys, $value) {
	if (is_array($keys) && if_empty($keys)) {
		return $array;
	}
	if (!is_array($keys)) {
		if (strstr($keys, "`")) {
			$keys = preg_replace("/`(.+)`/Ue", "str_replace('.','\.','\\1')", $keys);
		}
		$keys = preg_split("/(?<!\\\)\./", $keys);
	}
	if (count($keys) == 1) {
		$firstKey = array_pop($keys);
		$firstKey = str_replace("\.", ".", $firstKey);
		$array[$firstKey] = $value;
		return $array;
	}
	$lastKey = array_pop($keys);
	$lastKey = str_replace("\.", ".", $lastKey);
	$lastConfig = &$array;
	foreach ($keys as $key) {
		$key = str_replace("\.", ".", $key);
		if (!isset($lastConfig[$key]) || !is_array($lastConfig[$key])) {
			$lastConfig[$key] = array();
		}
		$lastConfig = &$lastConfig[$key];
	}
	$lastConfig[$lastKey] = $value;
	return $array;
}

/**
 * 安全地创建并写入文件
 * 
 * 如果文件已经存在，则会被删除，并重新创建
 *
 * @param string $filename 文件名
 * @param string $contents 要写入的内容
 * @param boolean $stripWhitespace 是否要去除代码中的空白，v1.1.0加入
 * @return boolean
 * @since 1.0.2
 */
function if_write_file($filename, $contents, $stripWhitespace = false) {
	$dir = dirname($filename);
	if ((!is_file($filename) || @unlink($filename)) && is_writeable($dir)) {
		$tmp = @tempnam($dir, "if_");
		if ($tmp) {
			$fd = @fopen($tmp, "w+");
			$created = false;
			if ($fd) {
				fwrite($fd, $contents);
				fclose($fd);
				if ($stripWhitespace) {
					$contents = php_strip_whitespace($tmp);
					@unlink($tmp);
					return if_write_file($filename, $contents);
				}
				$created = @rename($tmp, $filename);
			}
			if (!$created) {
				@unlink($tmp);
			}
			return $created;
		}
	}
	return false;
}

/**
 * 判断一个值是否在一个数组，或一个以逗号连接的字符串，或对象的属性中存在
 * 
 * - if_in_array(1, array(1, 2)); // => true
 * - if_in_array(1, "1,2"); // => true
 * - if_in_array(1, $obj);
 * 
 * @param mixed $value 值
 * @param mixed $array 数组、字符串或对象
 * @param boolean $strict 是否严格比较
 * @since 1.0.3
 */ 
function if_in_array($value, $array, $strict = false) {
	if (is_scalar($array)) {
		$array = preg_split("/\s*,\s*/", $array);
	}
	else if (is_object($array)) {
		$array = get_object_vars($array);
	}
	return (is_array($array) && in_array($value, $array, $strict));
}

/**
 * 将一个多维数组按照一个键的值排序
 *
 * @param array $array 数组
 * @param mixed $key string|array
 * @param boolean $asc 是否正排序
 * @return array
 * @since 1.0.3
 */
function if_array_sort(array $array, $key = null, $asc = true) {
	if (if_empty($key)) {
		$asc ? sort($array) : rsort($array);
	}
	else {
		$GLOBALS["IF_ARRAY_SORT_KEY_" . nil] = $key;
		usort($array, 
			$asc ? create_function('$p1,$p2', '$key=$GLOBALS["IF_ARRAY_SORT_KEY_" . nil];$p1=if_array_get($p1,$key);$p2=if_array_get($p2,$key);if ($p1>$p2){return 1;}elseif($p1==$p2){return 0;}else{return -1;}')
			:
			create_function('$p1,$p2', '$key=$GLOBALS["IF_ARRAY_SORT_KEY_" . nil];$p1=if_array_get($p1,$key);$p2=if_array_get($p2,$key);if ($p1<$p2){return 1;}elseif($p1==$p2){return 0;}else{return -1;}')
		);
		unset($GLOBALS["IF_ARRAY_SORT_KEY_" . nil]);
	}	
	return $array;
}

/**
 * 递归地合并两个多更多的数组
 * 
 * 与PHP内置的array_merge_recursive函数不同的地方是，如果第二个数组在某一级的值如果不是数组，则直接覆盖掉当前
 * 数组在那一级别的值
 *
 * @param array $array1 数组1
 * @param array $array2 数组2
 * @param array $arrayN 数组N
 * @return array
 * @since 1.1.2
 */
function if_array_merge_recursive(array $array1, array $array2) {
	if (func_num_args() == 2) {
		foreach ($array2 as $key => $value) {
			if (is_int($key)) {
				$array1[] = $value;
			}
			elseif (array_key_exists($key, $array1)) {
				$value1 = $array1[$key];
				if (is_array($value1) && is_array($value)) {
					$array1[$key] = if_array_merge_recursive($value1, $value);
				}
				else {
					$array1[$key] = $value;
				}
			}
			else {
				$array1[$key] = $value;
			}
		}
	}
	else {
		foreach (func_get_args() as $index => $arg) {
			if ($index > 0 && is_array($arg)) {
				$array1 = if_array_merge_recursive($array1, $arg);
			}
		}
	}
	return $array1;
}

?>